import tkinter as tk
from tkinter import ttk, messagebox
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import numpy as np
import math
from matplotlib.font_manager import FontProperties
import matplotlib as mpl

class WellCalculatorApp:
    def __init__(self, root):
        self.root = root
        self.root.title("沉井设计与计算程序")
        self.root.geometry("1200x800")
        
        # 设置Matplotlib使用支持中文的字体
        try:
            # 尝试使用微软雅黑
            plt.rcParams['font.sans-serif'] = ['Microsoft YaHei', 'SimHei', 'KaiTi', 'Arial Unicode MS']
        except:
            # 如果失败，尝试使用更通用的方法
            pass
    
        plt.rcParams['axes.unicode_minus'] = False  # 正常显示负号
        
        # 创建主框架
        self.main_frame = ttk.Frame(root)
        self.main_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=20)
        
        # 创建输入框架 - 改为三列布局
        self.input_frame = ttk.Frame(self.main_frame)
        self.input_frame.pack(fill=tk.X, padx=10, pady=10)
        
        # 创建三列框架
        self.basic_frame = ttk.LabelFrame(self.input_frame, text="基本参数")
        self.basic_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        self.segment_frame = ttk.LabelFrame(self.input_frame, text="分段参数")
        self.segment_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        self.section_frame = ttk.LabelFrame(self.input_frame, text="分节参数")
        self.section_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        # 创建基本参数输入区
        self.create_basic_inputs()
        
        # 创建分段参数输入区
        self.create_segment_inputs()
        
        # 创建分节参数输入区
        self.create_section_inputs()
        
        # 创建按钮区
        self.button_frame = ttk.Frame(self.main_frame)
        self.button_frame.pack(fill=tk.X, padx=10, pady=10)
        
        self.calculate_btn = ttk.Button(self.button_frame, text="计算并绘图", command=self.calculate)
        self.calculate_btn.pack(side=tk.LEFT, padx=5, pady=5)
        
        self.clear_btn = ttk.Button(self.button_frame, text="清空数据", command=self.clear_data)
        self.clear_btn.pack(side=tk.LEFT, padx=5, pady=5)
        
        # 创建结果展示区 - 扩大显示区域
        self.result_frame = ttk.LabelFrame(self.main_frame, text="计算结果")
        self.result_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        # 创建标签页
        self.notebook = ttk.Notebook(self.result_frame)
        self.notebook.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        # 分段结果标签页
        self.segment_tab = ttk.Frame(self.notebook)
        self.notebook.add(self.segment_tab, text="分段结果")
        
        # 分节结果标签页
        self.section_tab = ttk.Frame(self.notebook)
        self.notebook.add(self.section_tab, text="分节结果")
        
        # 绘图标签页
        self.plot_tab = ttk.Frame(self.notebook)
        self.notebook.add(self.plot_tab, text="标高示意图")
        
        # 初始化结果表格
        self.init_result_tables()
        
        # 初始化绘图区域
        self.init_plot_area()
        
        # 设置默认值
        self.set_default_values()
    
    def create_basic_inputs(self):
        # 基本参数输入 - 使用网格布局使其更紧凑
        basic_frame = ttk.Frame(self.basic_frame)
        basic_frame.pack(fill=tk.X, padx=5, pady=5)
        
        labels = [
            "井深度 (m):", "内径 (m):", 
            "井顶面标高 (m):", "设计地面标高 (m):", 
            "混凝土自重 (kN/m³):"
        ]
        self.basic_entries = []
        
        for i, label in enumerate(labels):
            row = i // 1  # 单列布局
            col = i % 1
            lbl = ttk.Label(basic_frame, text=label, width=18)
            lbl.grid(row=row, column=col*2, padx=3, pady=3, sticky=tk.W)
            
            entry = ttk.Entry(basic_frame, width=12)
            entry.grid(row=row, column=col*2+1, padx=3, pady=3)
            self.basic_entries.append(entry)
    
    def create_segment_inputs(self):
        # 分段参数输入 - 可动态调整数量
        segment_frame = ttk.Frame(self.segment_frame)
        segment_frame.pack(fill=tk.X, padx=5, pady=5)
        
        # 添加分段数量选择
        ttk.Label(segment_frame, text="分段数量:").grid(row=0, column=0, padx=5, pady=5)
        self.segment_count = tk.IntVar(value=4)
        segment_spinbox = ttk.Spinbox(segment_frame, from_=1, to=10, textvariable=self.segment_count, width=5)
        segment_spinbox.grid(row=0, column=1, padx=5, pady=5)
        # 使用trace代替bind来监控值变化
        self.segment_count.trace_add("write", lambda *args: self.update_segment_inputs())
        
        # 创建表头
        headers = ["分段", "长度 (m)", "壁厚 (m)"]
        for col, header in enumerate(headers):
            lbl = ttk.Label(segment_frame, text=header, font=('SimHei', 9, 'bold'))
            lbl.grid(row=1, column=col, padx=5, pady=2)
        
        # 存储输入框的框架
        self.segment_input_frame = ttk.Frame(segment_frame)
        self.segment_input_frame.grid(row=2, column=0, columnspan=3, sticky=tk.W)
        
        # 初始化分段输入框
        self.segment_entries = []
        self.update_segment_inputs()
    
    def create_section_inputs(self):
        # 分节参数输入 - 可动态调整数量
        section_frame = ttk.Frame(self.section_frame)
        section_frame.pack(fill=tk.X, padx=5, pady=5)
        
        # 添加分节数量选择
        ttk.Label(section_frame, text="分节数量:").grid(row=0, column=0, padx=5, pady=5)
        self.section_count = tk.IntVar(value=6)
        section_spinbox = ttk.Spinbox(section_frame, from_=1, to=10, textvariable=self.section_count, width=5)
        section_spinbox.grid(row=0, column=1, padx=5, pady=5)
        # 使用trace代替bind来监控值变化
        self.section_count.trace_add("write", lambda *args: self.update_section_inputs())
        
        # 创建表头
        headers = ["分节", "长度 (m)"]
        for col, header in enumerate(headers):
            lbl = ttk.Label(section_frame, text=header, font=('SimHei', 9, 'bold'))
            lbl.grid(row=1, column=col, padx=5, pady=2)
        
        # 存储输入框的框架
        self.section_input_frame = ttk.Frame(section_frame)
        self.section_input_frame.grid(row=2, column=0, columnspan=2, sticky=tk.W)
        
        # 初始化分节输入框
        self.section_entries = []
        self.update_section_inputs()
    
    def update_segment_inputs(self, event=None):
        # 保存当前输入值
        current_values = []
        for length_entry, thickness_entry in self.segment_entries:
            current_values.append((length_entry.get(), thickness_entry.get()))
        
        # 清空现有输入框
        for widget in self.segment_input_frame.winfo_children():
            widget.destroy()
        
        self.segment_entries = []
        count = self.segment_count.get()
        
        # 创建新的输入框
        for i in range(count):
            lbl = ttk.Label(self.segment_input_frame, text=f"第{i+1}段:")
            lbl.grid(row=i, column=0, padx=5, pady=2)
            
            length_entry = ttk.Entry(self.segment_input_frame, width=10)
            length_entry.grid(row=i, column=1, padx=5, pady=2)
            
            thickness_entry = ttk.Entry(self.segment_input_frame, width=10)
            thickness_entry.grid(row=i, column=2, padx=5, pady=2)
            
            # 恢复保存的值或设置默认值
            if i < len(current_values):
                length_val, thickness_val = current_values[i]
                length_entry.insert(0, length_val)
                thickness_entry.insert(0, thickness_val)
            else:
                length_entry.insert(0, "0")
                thickness_entry.insert(0, "0.5")
            
            self.segment_entries.append((length_entry, thickness_entry))
        
        # 如果是从clear_data调用的，不设置默认值
        if event is not None:
            self.set_segment_default_values()
    
    def update_section_inputs(self, event=None):
        # 保存当前输入值
        current_values = [entry.get() for entry in self.section_entries]
        
        # 清空现有输入框
        for widget in self.section_input_frame.winfo_children():
            widget.destroy()
        
        self.section_entries = []
        count = self.section_count.get()
        
        # 创建新的输入框
        for i in range(count):
            lbl = ttk.Label(self.section_input_frame, text=f"第{i+1}节:")
            lbl.grid(row=i, column=0, padx=5, pady=2)
            
            length_entry = ttk.Entry(self.section_input_frame, width=10)
            length_entry.grid(row=i, column=1, padx=5, pady=2)
            
            # 恢复保存的值或设置默认值
            if i < len(current_values):
                length_entry.insert(0, current_values[i])
            else:
                length_entry.insert(0, "0")
            
            self.section_entries.append(length_entry)
        
        # 如果是从clear_data调用的，不设置默认值
        if event is not None:
            self.set_section_default_values()
    
    def init_result_tables(self):
        # 分段结果表格
        segment_columns = ("分段", "底标高 (m)", "顶标高 (m)", "壁厚 (m)", "外径 (m)", 
                          "自重 (kN)", "外表面积 (㎡)", "投影面积 (㎡)")
        self.segment_tree = ttk.Treeview(self.segment_tab, columns=segment_columns, show="headings")
        
        for col in segment_columns:
            self.segment_tree.heading(col, text=col)
            self.segment_tree.column(col, width=100, anchor=tk.CENTER)
        
        self.segment_tree.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        # 分节结果表格
        section_columns = ("分节", "底标高 (m)", "顶标高 (m)", "自重 (kN)", 
                          "外表面积 (㎡)", "投影面积 (㎡)")
        self.section_tree = ttk.Treeview(self.section_tab, columns=section_columns, show="headings")
        
        for col in section_columns:
            self.section_tree.heading(col, text=col)
            self.section_tree.column(col, width=100, anchor=tk.CENTER)
        
        self.section_tree.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
    
    def init_plot_area(self):
        # 增大绘图区域
        self.fig = plt.Figure(figsize=(10, 7), dpi=100)
        self.ax = self.fig.add_subplot(111)
        
        self.canvas = FigureCanvasTkAgg(self.fig, self.plot_tab)
        self.canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)
        
        # 添加初始文本说明
        self.ax.text(0.5, 0.5, "点击'计算并绘图'按钮生成标高示意图", 
                    ha='center', va='center', fontsize=12, color='gray')
        self.ax.axis('off')
        self.canvas.draw()
    
    def set_default_values(self):
        # 设置默认值
        self.basic_entries[0].insert(0, "37.782")
        self.basic_entries[1].insert(0, "14.5")
        self.basic_entries[2].insert(0, "22.7")
        self.basic_entries[3].insert(0, "24.7")
        self.basic_entries[4].insert(0, "25")
        
        # 设置分段和分节的默认值
        self.set_segment_default_values()
        self.set_section_default_values()
    
    def set_segment_default_values(self):
        # 分段默认值
        segment_values = [
            ("10", "1.5"),
            ("10", "1.3"),
            ("10", "1.1"),
            ("7.782", "0.9")
        ]
        
        count = min(len(segment_values), len(self.segment_entries))
        for i in range(count):
            length, thickness = segment_values[i]
            self.segment_entries[i][0].delete(0, tk.END)
            self.segment_entries[i][0].insert(0, length)
            self.segment_entries[i][1].delete(0, tk.END)
            self.segment_entries[i][1].insert(0, thickness)
        
        # 对于额外的分段，设置默认值
        for i in range(count, len(self.segment_entries)):
            self.segment_entries[i][0].delete(0, tk.END)
            self.segment_entries[i][0].insert(0, "0")
            self.segment_entries[i][1].delete(0, tk.END)
            self.segment_entries[i][1].insert(0, "0.5")
    
    def set_section_default_values(self):
        # 分节默认值
        section_values = ["8.7", "6", "6", "6", "6", "5.082"]
        
        count = min(len(section_values), len(self.section_entries))
        for i in range(count):
            self.section_entries[i].delete(0, tk.END)
            self.section_entries[i].insert(0, section_values[i])
        
        # 对于额外的分节，设置默认值
        for i in range(count, len(self.section_entries)):
            self.section_entries[i].delete(0, tk.END)
            self.section_entries[i].insert(0, "0")
    
    def clear_data(self):
        # 清空所有输入框
        for entry in self.basic_entries:
            entry.delete(0, tk.END)
        
        for entries in self.segment_entries:
            for entry in entries:
                entry.delete(0, tk.END)
        
        for entry in self.section_entries:
            entry.delete(0, tk.END)
        
        # 清空结果表格
        for item in self.segment_tree.get_children():
            self.segment_tree.delete(item)
        
        for item in self.section_tree.get_children():
            self.section_tree.delete(item)
        
        # 清空图形
        self.ax.clear()
        self.ax.text(0.5, 0.5, "点击'计算并绘图'按钮生成标高示意图", 
                    ha='center', va='center', fontsize=12, color='gray')
        self.ax.axis('off')
        self.canvas.draw()
        
        # 恢复默认值
        self.set_default_values()
    
    def calculate(self):
        # 检查分段和分节总长度是否一致
        try:
            # 计算分段总长度
            segment_total = sum(float(entry[0].get()) for entry in self.segment_entries if entry[0].get())
            # 计算分节总长度
            section_total = sum(float(entry.get()) for entry in self.section_entries if entry.get())
            
            # 检查总长度是否一致（考虑浮点数精度问题）
            if not abs(segment_total - section_total) < 1e-6:
                messagebox.showerror("输入错误", 
                                    f"分段总长度({segment_total:.3f}m)与分节总长度({section_total:.3f}m)不相等，请检查并修改。")
                return
        except ValueError:
            messagebox.showerror("输入错误", "请确保所有长度输入为有效的数字。")
            return
        
        try:
            # 获取基本参数
            depth = float(self.basic_entries[0].get())
            inner_diameter = float(self.basic_entries[1].get())
            top_elevation = float(self.basic_entries[2].get())
            ground_elevation = float(self.basic_entries[3].get())
            concrete_weight = float(self.basic_entries[4].get())
            
            # 获取分段参数
            segments = []
            for i, (length_entry, thickness_entry) in enumerate(self.segment_entries):
                length = float(length_entry.get())
                thickness = float(thickness_entry.get())
                segments.append({"length": length, "thickness": thickness})
            
            # 获取分节参数
            sections = []
            for i, length_entry in enumerate(self.section_entries):
                length = float(length_entry.get())
                sections.append({"length": length})
            
            # 计算井底标高
            bottom_elevation = top_elevation - depth
            
            # 计算各段信息
            current_elevation = bottom_elevation
            segment_results = []
            for i, seg in enumerate(segments):
                seg["bottom_elevation"] = current_elevation
                seg["top_elevation"] = current_elevation + seg["length"]
                seg["outer_diameter"] = inner_diameter + 2 * seg["thickness"]
                
                # 计算体积
                outer_radius = seg["outer_diameter"] / 2
                inner_radius = inner_diameter / 2
                seg["volume"] = math.pi * seg["length"] * (outer_radius**2 - inner_radius**2)
                
                seg["weight"] = seg["volume"] * concrete_weight
                seg["outer_surface"] = math.pi * seg["outer_diameter"] * seg["length"]
                seg["projection_area"] = math.pi * (seg["outer_diameter"] / 2) ** 2
                
                current_elevation = seg["top_elevation"]
                segment_results.append((
                    f"第{i+1}段", 
                    f"{seg['bottom_elevation']:.3f}",
                    f"{seg['top_elevation']:.3f}",
                    f"{seg['thickness']:.3f}",
                    f"{seg['outer_diameter']:.3f}",
                    f"{seg['weight']:.2f}",
                    f"{seg['outer_surface']:.2f}",
                    f"{seg['projection_area']:.2f}"
                ))
            
            # 计算各节信息
            current_elevation = bottom_elevation
            section_results = []
            for i, sec in enumerate(sections):
                sec["bottom_elevation"] = current_elevation
                sec["top_elevation"] = current_elevation + sec["length"]
                
                # 确定节所在的段及其参数
                volume = 0
                outer_surface = 0
                projection_area = 0
                
                for seg in segments:
                    # 检查节与段是否有重叠
                    overlap_bottom = max(sec["bottom_elevation"], seg["bottom_elevation"])
                    overlap_top = min(sec["top_elevation"], seg["top_elevation"])
                    overlap_length = max(0, overlap_top - overlap_bottom)
                    
                    if overlap_length > 0:
                        outer_diameter = seg["outer_diameter"]
                        
                        # 计算重叠部分的体积和表面积
                        outer_radius = outer_diameter / 2
                        inner_radius = inner_diameter / 2
                        seg_volume = math.pi * overlap_length * (outer_radius**2 - inner_radius**2)
                        seg_surface = math.pi * outer_diameter * overlap_length
                        
                        volume += seg_volume
                        outer_surface += seg_surface
                        
                        # 投影面积取节底部所在段的值
                        if sec["bottom_elevation"] >= seg["bottom_elevation"] and sec["bottom_elevation"] < seg["top_elevation"]:
                            projection_area = math.pi * (outer_diameter / 2) ** 2
                
                sec["weight"] = volume * concrete_weight
                sec["outer_surface"] = outer_surface
                sec["projection_area"] = projection_area
                
                current_elevation = sec["top_elevation"]
                section_results.append((
                    f"第{i+1}节", 
                    f"{sec['bottom_elevation']:.3f}",
                    f"{sec['top_elevation']:.3f}",
                    f"{sec['weight']:.2f}",
                    f"{sec['outer_surface']:.2f}",
                    f"{sec['projection_area']:.2f}"
                ))
            
            # 更新结果表格
            for item in self.segment_tree.get_children():
                self.segment_tree.delete(item)
            
            for result in segment_results:
                self.segment_tree.insert("", tk.END, values=result)
            
            for item in self.section_tree.get_children():
                self.section_tree.delete(item)
            
            for result in section_results:
                self.section_tree.insert("", tk.END, values=result)
            
            # 更新图形
            self.plot_results(segments, sections, top_elevation, ground_elevation)
            
        except ValueError as e:
            messagebox.showerror("输入错误", "请输入有效的数值参数！")
    
    def plot_results(self, segments, sections, top_elevation, ground_elevation):
        self.ax.clear()
        
        # 绘制井体
        colors = ['#4e79a7', '#f28e2b', '#e15759', '#76b7b2', '#59a14f', '#edc948']
        
        # 绘制分段
        for i, seg in enumerate(segments):
            y_bottom = seg['bottom_elevation']
            y_top = seg['top_elevation']
            outer_diameter = seg['outer_diameter']
            
            # 绘制外轮廓
            self.ax.fill_between([-outer_diameter/2, outer_diameter/2], 
                                [y_bottom, y_bottom], [y_top, y_top],
                                color=colors[i % len(colors)], alpha=0.7)
            
            # 绘制内轮廓
            inner_diameter = outer_diameter - 2 * seg['thickness']
            self.ax.fill_between([-inner_diameter/2, inner_diameter/2], 
                                [y_bottom, y_bottom], [y_top, y_top],
                                color='white', alpha=1.0)
        
        # 标注分段
        for i, seg in enumerate(segments):
            y_mid = (seg['bottom_elevation'] + seg['top_elevation']) / 2
            self.ax.text(0, y_mid, f"第{i+1}段\n壁厚:{seg['thickness']}m", 
                        ha='center', va='center', fontsize=9, color='black')
        
        # 绘制分节边界
        for i, sec in enumerate(sections):
            y = sec['top_elevation']
            self.ax.axhline(y=y, color='black', linestyle='--', linewidth=1, alpha=0.7)
            self.ax.text(segments[0]['outer_diameter']/2 + 1, y, 
                        f"第{i+1}节顶\n标高: {y:.3f}m", 
                        ha='left', va='center', fontsize=8)
        
        # 标注井底
        bottom_elevation = segments[0]['bottom_elevation']
        self.ax.text(segments[0]['outer_diameter']/2 + 1, bottom_elevation, 
                    f"井底标高: {bottom_elevation:.3f}m", 
                    ha='left', va='center', fontsize=9, color='red')
        
        # 标注井顶
        self.ax.text(segments[0]['outer_diameter']/2 + 1, top_elevation, 
                    f"井顶标高: {top_elevation:.3f}m", 
                    ha='left', va='center', fontsize=9, color='red')
        
        # 标注地面
        self.ax.axhline(y=ground_elevation, color='green', linewidth=2, linestyle='-', label='设计地面')
        self.ax.text(segments[0]['outer_diameter']/2 + 1, ground_elevation, 
                    f"设计地面标高: {ground_elevation:.3f}m", 
                    ha='left', va='center', fontsize=9, color='green')
        
        # 设置图形属性
        self.ax.set_title('沉井标高示意图', fontsize=14)
        self.ax.set_xlabel('直径 (m)', fontsize=10)
        self.ax.set_ylabel('标高 (m)', fontsize=10)
        self.ax.grid(True, linestyle='--', alpha=0.7)
        self.ax.legend(loc='upper right')
        
        # 调整坐标轴范围
        max_diameter = max(seg['outer_diameter'] for seg in segments)
        self.ax.set_xlim(-max_diameter*0.7, max_diameter*0.7)
        
        self.ax.set_ylim(bottom_elevation - 5, max(ground_elevation, top_elevation) + 5)
        
        self.canvas.draw()

if __name__ == "__main__":
    root = tk.Tk()
    app = WellCalculatorApp(root)
    root.mainloop()